

import java.util.ArrayList;
import java.util.List;

/**
 *	This parameterized class defines a CSP variable. 
 *
 * @param <Type> The variable will be given a certain type of values that we 
 * don't know about yet. Such a type will be specified at some later point 
 * either by subclassing or at runtime when instantiating
 */
public abstract class Variable<Type> {
	
	/**
	 * String to specify the name of the variable.
	 */
	private String name;
	
	/**
	 * Will contain the value assigned to the variable.
	 */
	private Type value;
	
	/**
	 * List<Constraint<Type>> containing the constraints that 
	 * involve this variable.
	 */
	private List<Constraint<Type>> constraints;
	
	/**
	 * List<Type> containing the variable's value domain.
	 */
	private List<Type> domain;
	
	/**
	 * List<Type> containing the values that haven't been tried on the 
	 * variable yet.
	 */
	private List<Type> unassignedValues;

	/**
	 * Constructor method
	 * 
	 * @param name String specifying the variable's name.
	 * @param domain List<Type> having the variable's value domain.
	 */
	public Variable(String name, List<Type> domain) {
		this.name = name;
		this.domain = domain;
		this.resetUnassignedValues();
		this.constraints = new ArrayList<Constraint<Type>>();
	}

	/**
	 * Returns the variable's name.
	 * 
	 * @return String specifying the variable's name.
	 */
	public String getName() {
		return name;
	}

	/**
	 * Returns the value assigned to the variable.
	 * 
	 * @return Type, the specific variable's value.
	 */
	public Type getValue() {
		return value;
	}

	/**
	 * Assigns a value to the variable.
	 * 
	 * @param value specifying the value to be assigned to the variable.
	 */
	public void setValue(Type value) {
		this.value = value;
	}

	/**
	 * Checks whether the variable is assigned.
	 * 
	 * @return
	 * 	<ul>
	 * 		<li><b>true</b> - If the variable is assigned.	
	 * 		<li><b>false</b> - If the variable is not assigned.
	 * </ul> 
	 */	
	public boolean hasValue() {
		return this.value != null;
	}

	/**
	 * Resets the variable's value to <b>null</b>.
	 */
	public void resetValue() {
		this.value = null;
	}

	/**
	 * Returns the list of constraints involving this variable.
	 * 
	 * @return List<onstraint<Type>> specifying the constraints that 
	 * involve this variable.
	 */
	public List<Constraint<Type>> getConstraints() {
		return constraints;
	}

	/**
	 * Updates the list of constraints involving this variable.
	 * 
	 * @param constraints List<onstraint<Type>> specifying the constraints that involve 
	 * this variable.
	 */
	
	public void setConstraints(List<Constraint<Type>> constraints) {
		this.constraints = constraints;
	}
	
	/**
	 * Adds a new constraint involving this variable.
	 * 
	 * @param constraint Constraint<Type> specifying the new constraint.
	 */	
	public void addConstraint(Constraint<Type> constraint) {
		if (constraint != null) {
			this.constraints.add(constraint);
		}
	}	

	/**
	 * Returns the variable's value domain.
	 * 
	 * @return List<Type> specifying the variable's value domain
	 */	
	public List<Type> getDomain() {
		return domain;
	}
	
	/**
	 * Updates the variable's value domain.
	 * 
	 * @param domain List<Type> specifying the variable's value domain
	 */
	
	public void setDomain(List<Type> domain) {
		this.domain = domain;
	}
	
	/**
	 * Adds a new value to the variable's value domain.
	 * 
	 * @param value Type, specifying the new value.
	 */	
	public void addDomainValue(Type value) {
		if (value != null && !this.domain.contains(value)) {
			this.domain.add(value);
		}
	}

	/**
	 * Returns the list of values that haven't been tried on the variable yet.
	 * 
	 * @return List<Type> specifying the values remaining to be tried.
	 */
	public List<Type> getUnassignedValues() {
		return unassignedValues;
	}

	/**
	 * Returns the next value to be tried on the variable.
	 * 
	 * @return 
	 * 	<ul>
	 * 		<li> Type specifying the next value to be tried on the variable.</li>
	 *  	<li><b>null</b> - If there are no more values to be tried.</li>
	 *  </ul>
	 */	
	public Type getNextUnassignedValue() {
		if (this.hasUnassignedValues()) {
			return this.unassignedValues.remove(0);
		} else {
			return null;
		}
	}

	/**
	 * Checks whether there are still values to be tried on the variable.
	 * 
	 * @return
	 * 	<ul>
	 * 		<li>true - if there are any values to be tried yet.</li>
	 *  	<li>true - if all values have been tried.</li>
	 *  </ul> 
	 */	
	public boolean hasUnassignedValues() {
		return !this.unassignedValues.isEmpty();
	}	
	
	/**
	 * The list of values to be tried is reset to be the complete value domain.
	 */		
	public void resetUnassignedValues() {		
		this.unassignedValues = new ArrayList<Type>();
		
		for (Type valor : this.domain) {
			this.unassignedValues.add(valor);
		}
	}	

	/**
	 * Returns a string representation of the object. In general, the toString
	 * method returns a string that "textually represents" this object.
	 * 
	 * The result should be a concise but informative representation that is 
	 * easy for a person to read. It is recommended that all subclasses 
	 * override this method.
	 * 
	 * The toString method for class Object returns a string consisting of the
	 * name of the class of which the object is an instance, the at-sign 
	 * character `@', and the unsigned hexadecimal representation of the hash
	 * code of the object. 
	 * 
	 * In other words, this method returns a string equal to the value of: 
	 * getClass().getName() + '@' + Integer.toHexString(hashCode())
	 * 
	 * @return a string representation of the object.
	 */
	public String toString() {
		String result = this.name + " = ";

		if (this.value != null) {
			result += this.value;
		} else {
			result += "?";
		}

		return result;
	}
	
	/**
	 * Indicates whether some other object is "equal to" this one.
	 * 
	 * The equals method for class Object implements the most discriminating 
	 * possible equivalence relation on objects; that is, for any non-null 
	 * reference values x and y, this method returns true if and only if x 
	 * and y refer to the same object (x == y has the value true).
	 * 
	 * 
	 * @param obj
	 *            the reference object with which to compare.
	 * @return boolean
	 *         <li><b>true</b> - if this object is the same as the obj argument.</li>
	 *         <li><b>false</b> - otherwise.</li>
	 */
	public boolean equals(Object obj) {
		if ( obj != null && obj instanceof Variable<?>) { 
			return ((Variable<?>) obj).name.equals(this.name);
		} else {
			return false;
		}
	}	
}